
import pygame.draw
import math

LARGE_SIZE = (200,200)
MEDIUM_SIZE = (150,150)
SMALL_SIZE = (40,40)

registrars = []

class Widget(object):

    def __init__(self,x,y,w,h,layer=0):
        self.size = (w,h)
        self.location = (x,y)
        self.behaviors = []
        self.fixed = False
        self.offset = (0,0)
        if len(registrars):
            for r in registrars:
                r.addWidget(self,layer)

    def remove(self):
        if len(registrars):
            for r in registrars:
                r.removeWidget(self)

    def add(self,layer):
        if len(registrars):
            for r in registrars:
                r.addWidget(self,layer)

    def addBehavior(self,b):
        for x in self.behaviors:
            if isinstance(b,type(x)):
                self.behaviors.remove(x)
        self.behaviors.append(b)

    def animate(self):
        remove = []
        for b in self.behaviors:
            if not b.animate(self):
                remove.append(b)
        for b in remove:
            self.behaviors.remove(b)

    def pointIn(self,x,y):
        x = x + self.offset[0]
        y = y + self.offset[1]
        
        if x > self.location[0] and y > self.location[1] and \
           x < self.location[0]+self.size[0] and y < self.location[1]+self.size[1]:
            return True
        return False

    def draw(self, screen, transformx, transformy):
        if self.fixed:
            (localx,localy) = self.location
        else:
            localx = self.location[0] - transformx
            localy = self.location[1] - transformy
            self.offset = (transformx, transformy)

        (scrx,scry) = screen.get_size()

        if localx + self.size[0] < 0 \
           or localy + self.size[1] < 0 \
           or localx > scrx \
           or localy > scry:
            # we're outside the screen
            return
        self.paint(screen,localx,localy)

    def paint(self,screen,realx,realy):
        # override me to really paint something useful
        pygame.draw.arc(screen, (0,0,255), (realx, realy, self.size[0], self.size[1]), 0, 2*math.pi, 2.5)
        pygame.draw.rect(screen, (255,0,0), (realx, realy, self.size[0], self.size[1]), 2.5)

        
    def processEvent(self,evt):
        # return True of you processed the event
        return False

class Label(Widget):

    def __init__(self, (x,y), text, font, fgcolor=(255,255,255), bgcolor=None, layer=0):
        if bgcolor is None:
            self.text = font.render(text, True, fgcolor)
        else:
            self.text = font.render(text, True, fgcolor, bgcolor)
        sz = self.text.get_size()
        Widget.__init__(self, x, y, sz[0], sz[1], layer)

    def paint(self,screen,realx,realy):
        screen.blit(self.text, (realx,realy))

class OpaqueBackground(Widget):
    def __init__(self, (x,y,w,h), layer, color=(255,255,255), alpha=128):
        Widget.__init__(self,x,y,w,h,layer)
        self.surface = pygame.Surface( (w,h) )
        self.surface.fill(color)
        self.surface.set_alpha(alpha)

    def paint(self,screen,realx,realy):
        screen.blit(self.surface, (realx,realy))

    def processEvent(self,evt):
        if evt.type in [pygame.MOUSEMOTION,pygame.MOUSEBUTTONUP,pygame.MOUSEBUTTONDOWN]:
            (x,y) = evt.pos
            if self.pointIn(x,y):
                return True
        return False

class MouseButton(Widget):

    def __init__(self,x,y,w,h,layer=0):
        Widget.__init__(self,x,y,w,h,layer)
        self.mouseIn = False

    def clicked(self):
        pass

    def processEvent(self,evt):
        if evt.type in [pygame.MOUSEMOTION,pygame.MOUSEBUTTONUP,pygame.MOUSEBUTTONDOWN]:
            (x,y) = evt.pos
            if self.pointIn(x,y):
                self.mouseIn = True
                if evt.type is pygame.MOUSEBUTTONUP:
                    self.clicked()
                return True
            else:
                self.mouseIn = False

        return False
        
class ClickableLabel(MouseButton):

    def __init__(self, eventinfo, (x,y), text, font, fgcolor=(200,200,200), hovercolor=(255,255,255), bgcolor=None, layer=0):
        discolor = (fgcolor[0]/2,fgcolor[1]/2,fgcolor[2]/2)
        if bgcolor is None:
            self.text = font.render(text, True, fgcolor)
            self.hovertext = font.render(text, True, hovercolor)
            self.disabledtext = font.render(text, True, discolor)
        else:
            self.text = font.render(text, True, fgcolor, bgcolor)
            self.hovertext = font.render(text, True, hovercolor, bgcolor)
            self.disabledtext = font.render(text, True, discolor, bgcolor)
        sz = self.text.get_size()
        self.fgcolor = fgcolor
        self.eventinfo = eventinfo
        self.eventinfo['label'] = self
        self.enabled = True
        MouseButton.__init__(self, x, y, sz[0], sz[1], layer)

    def paint(self,screen,realx,realy):
        if not self.enabled:
            screen.blit(self.disabledtext, (realx,realy))
        elif self.mouseIn:
            screen.blit(self.hovertext, (realx,realy))
            pygame.draw.rect(screen, self.fgcolor, (realx-5,realy-1,self.hovertext.get_size()[0]+10,self.hovertext.get_size()[1]+2), 1)
        else:
            screen.blit(self.text, (realx,realy))
        
    def clicked(self):
        if self.enabled:
            pygame.event.post(pygame.event.Event(pygame.USEREVENT,
                                                 self.eventinfo
                                                 ))


class TrackCover(MouseButton):

    def __init__(self,albumcover,track,font,x,y,layer):
        MouseButton.__init__(self,x,y,SMALL_SIZE[0],SMALL_SIZE[1],layer)
        self.albumcover = albumcover
        self.track = track
        self.icon = pygame.transform.scale(self.albumcover.album.artwork(), SMALL_SIZE)
        number = font.render(str(track.number()), True, (255,255,255),(0,0,0))
        number.set_alpha(128)
        x = (SMALL_SIZE[0] - number.get_size()[0]) / 2
        y = (SMALL_SIZE[1] - number.get_size()[1]) / 2
        self.icon.blit(number,(x,y))
        
    def paint(self,screen,realx,realy):
        screen.blit(self.icon, (realx, realy) )
        
class AlbumCover(MouseButton):

    def __init__(self,album,font):
        MouseButton.__init__(self,0,0,MEDIUM_SIZE[0],MEDIUM_SIZE[1])

        self.font = font
        self.album = album
        self.cover_small = pygame.transform.scale(self.album.artwork(), SMALL_SIZE)
        self.cover_medium = pygame.transform.scale(self.album.artwork(), MEDIUM_SIZE)
        self.cover_large = pygame.transform.scale(self.album.artwork(), LARGE_SIZE)
        self.selected = False
        self.tracks = None

    def clicked(self):
        pygame.event.post(pygame.event.Event(pygame.USEREVENT,
                                             {'album':self.album,
                                              'cover':self,
                                              'juketype':'ALBUM_CLICKED'
                                              }))

    def paint(self,screen,realx,realy):
        # fixme; resize the closest size instead
        if self.size == SMALL_SIZE:
            c = self.cover_small
        elif self.size == MEDIUM_SIZE:
            c = self.cover_medium
        elif self.size == LARGE_SIZE:
            c = self.cover_large
        else:
            c = pygame.transform.scale(self.cover_large, self.size)
        screen.blit(c, (realx, realy) )

        if 1:
            if self.mouseIn:
                pygame.draw.rect(screen, (255,0,0), (realx-1,realy-1,self.size[0]+1,self.size[1]+1),2)

        if self.selected:
            diffx = (LARGE_SIZE[0] - MEDIUM_SIZE[0])/2
            diffy = (LARGE_SIZE[1] - MEDIUM_SIZE[1])/2
            screen.blit( self.cover_large, (realx-diffx, realy-diffy))

            if 0:
                self.background = pygame.Surface((trackWidth+10, len(self.tracks) * self.tracks[0].get_size()[1]))
                self.background.fill( (0,100,0) )
                self.background.set_alpha(200)

            x = realx - diffx + LARGE_SIZE[0] + 5
            y = realy - diffy
            if 0:
                screen.blit(self.background, (x-5,y))
                for t in self.tracks:
                    screen.blit(t, (x,y))
                    y = y + t.get_size()[1]

    def select(self):
        self.selected = True
        if self.tracks is None:
            self.tracks = []
            trackWidth = 0
            diffx = (LARGE_SIZE[0] - MEDIUM_SIZE[0])/2
            diffy = (LARGE_SIZE[1] - MEDIUM_SIZE[1])/2
            x = self.location[0] + LARGE_SIZE[0] - diffx + 5
            y = self.location[1] - diffy + 5
            for t in self.album.tracks:
                foo = ClickableLabel( {'album':self.album,
                                       'cover':self,
                                       'track':t,
                                       'juketype':'TRACK_CLICKED'},
                                      (x,y), '%d - '%t.number() + t.name(), self.font, (200,200,200), (255,255,255), None, 2)
                self.tracks.append(foo)
                if foo.size[0] > trackWidth:
                    trackWidth = foo.size[0]
                y = y + foo.size[1]
            self.tracksbg = OpaqueBackground( (x-5,self.location[1]-diffy,trackWidth+10,len(self.tracks)*self.tracks[0].size[1]+10), 1, (0,100,0), 175 )

        else:
            diffx = (LARGE_SIZE[0] - MEDIUM_SIZE[0])/2
            diffy = (LARGE_SIZE[1] - MEDIUM_SIZE[1])/2
            x = self.location[0] + LARGE_SIZE[0] - diffx + 5
            y = self.location[1] - diffy + 5
            self.tracksbg.location = (x-5,y-5)
            for t in self.tracks:
                if t.eventinfo['track'].isQueued():
                    t.enabled = False
                else:
                    t.enabled = True
                t.add(2)
                t.location = (x,y)
                y = y + t.size[1]
            self.tracksbg.add(1)
                    
    def unSelect(self):
        self.selected = False
        if self.tracks:
            for t in self.tracks:
                t.remove()
            self.tracksbg.remove()

class ToggleButton(MouseButton):
    
    def __init__(self,(x,y),on,off,mousein=None):
        (w,h) = on.get_size()
        MouseButton.__init__(self,x,y,w,h)
        self.on = on
        self.off = off
        self.mouse = mousein
        self.state = False

    def clicked(self):
        self.state = not self.state
        
    def paint(self,screen,realx,realy):
        if self.mouse and self.mouseIn and not self.state:
            screen.blit( self.mouse, (realx, realy) )
        else:
            if self.state:
                screen.blit( self.on, (realx, realy) )
            else:
                screen.blit( self.off, (realx, realy) )
        

class RadioButton(ToggleButton):

    def __init__(self,(x,y),on,off,mousein=None,others=[]):
        ToggleButton.__init__(self,(x,y),on,off,mousein)
        if len(others):
            self.group = others
        else:
            self.group = []

    def addToGroup(self,g):
        if not g in self.group:
            self.group.append(g)
            g.addToGroup(self)

    def removeFromGroup(self,g):
        self.group.remove(g)
    
    def clicked(self):
        if self.state is False:
            pygame.event.post(pygame.event.Event(pygame.USEREVENT,
                                                 {'button':self,
                                                  'juketype':'BUTTON_CLICKED'
                                                  }))
        self.state = True
        for x in self.group:
            x.state = False

class TextRadioButton(RadioButton):

    def __init__(self, (x,y), text, font, others=[]):
        RadioButton.__init__(self, (x,y),
                             font.render(text, True, (255,255,255), (0,0,0)),
                             font.render(text, True, (100,100,100), (0,0,0)),
                             font.render(text, True, (255,100,100), (0,0,0)),
                             others)







class Behavior(object):

    def animate(self,widget):
        pass

def distance( (x,y), (a,b) ):
    return math.sqrt( (x-a)*(x-a) + (y-b)*(y-b) )


class Fly(Behavior):

    def __init__(self,targetx,targety):
        self.target = (int(targetx),int(targety))

    def animate(self,widget):
        if widget.location[0] == self.target[0] \
           and widget.location[1] == self.target[1]:
            return False

        ratio = 2.0
        if distance(self.target, widget.location) > 50:
            ration = 8.0
        deltax = (self.target[0] - widget.location[0]) / ratio
        deltay = (self.target[1] - widget.location[1]) / ratio
        if 1:
            LIMIT = 50
            if deltax > LIMIT or deltay > LIMIT:
                if deltay == 0:
                    deltax = LIMIT
                elif deltax == 0:
                    deltay = LIMIT
                else:
                    ratio = deltax/float(deltay)
                    if deltax > deltay:
                        deltay = LIMIT / ratio
                        deltax = LIMIT
                    else:
                        deltax = LIMIT * ratio
                        deltay = LIMIT

        if int(deltax) == 0 or int(deltay) == 0:
            widget.location = self.target
            return False

        widget.location = (int(widget.location[0]+deltax), int(widget.location[1]+deltay))

        return True
        
class SlowFly(Fly):

    def __init__(self,targetx,targety):
        Fly.__init__(self,targetx,targety)

    def animate(self,widget):
        if widget.location[0] == self.target[0] \
           and widget.location[1] == self.target[1]:
            return False

        ratio = 2.0
        if distance(self.target, widget.location) > 50:
            ration = 8.0
        deltax = (self.target[0] - widget.location[0]) / ratio
        deltay = (self.target[1] - widget.location[1]) / ratio
        if 1:
            LIMIT = 50
            if deltax > LIMIT or deltay > LIMIT:
                if deltay == 0:
                    deltax = LIMIT
                elif deltax == 0:
                    deltay = LIMIT
                else:
                    ratio = deltax/float(deltay)
                    if deltax > deltay:
                        deltay = LIMIT / ratio
                        deltax = LIMIT
                    else:
                        deltax = LIMIT * ratio
                        deltay = LIMIT

        if int(deltax) == 0 or int(deltay) == 0:
            widget.location = self.target
            return False

        widget.location = (int(widget.location[0]+deltax), int(widget.location[1]+deltay))

        return True
        
        
class Resize(Behavior):

    def __init__(self,newsize,frames):
        self.newsize = newsize
        self.frames = float(frames)
        self.frame = 0
        self.w = None
        self.h = None

    def animate(self,widget):
        if self.w is None or self.h is None:
            self.w = widget.size[0]
            self.h = widget.size[1]

        deltaw = float(self.w - self.newsize[0]) / self.frames
        deltah = float(self.h - self.newsize[1]) / self.frames
        widget.size = (widget.size[0] - deltaw, widget.size[1] - deltah)
        
        self.frame = self.frame + 1
        if self.frame >= self.frames:
            #widget.size = (self.w, self.h)
            return False

        return True

        
